<template>
  <div class="flow-containers view-mode" style="height: 100%" >
    <a-layout style="height: 100%">
      <a-layout style="align-items: stretch;">
        <a-layout-content style="padding: 0;margin-top: 10px;">
          <div ref="canvas" class="canvas" :style="{minHeight:height+'px'}"/>
          <a-drawer
            :visible="drawerVisible"
            title="流程信息"
            :width="450"
            placement="right"
            :closable="true"
            @close="drawerVisible = false"
          >
            <a-timeline>
              <a-timeline-item v-for="o in currentTaskList" :key="o.id">
                <p>开始时间: {{ o.startTime }}</p>
                <p>状态：{{ stateNameConvert(o.state) }}</p>
                <p>处理结果：{{ dictConvert('BpmTaskResult',o.result) }}</p>
                <p>处理人: {{ o.userName }}</p>
                <p>结束时间: {{ o.endTime? o.endTime : '' }}</p>
                <p>审批意见：{{ o.reason ? o.reason : '' }}</p>
              </a-timeline-item>
            </a-timeline>
          </a-drawer>
        </a-layout-content>
        <a-layout-sider class="sider" style="background: #fff; min-width: 40px; width: 40px;max-width: 40px;border-left: 1px solid #eeeeee;box-shadow: 0 0 8px #cccccc;">
        </a-layout-sider>
      </a-layout>
    </a-layout>
  </div>
</template>

<script>
// 汉化
import customTranslate from './common/customTranslate'
import Modeler from 'bpmn-js/lib/Modeler'
// 引入flowable的节点文件
import flowableModdle from './flowable/flowable.json'
import { addArrow } from '@/components/Bpmn/processViewerUtils'
import { dictConvert } from '@/components/Bootx/Dict/DictUtils'

export default {
  name: 'WorkflowBpmnModeler',
  props: {
    // bpmn流程图
    xml: { type: String, default: '' },
    // 实例
    instance: { type: Object, default: () => {} },
    // 执行流程节点
    flowNodeList: { type: Array, default: () => [] },
    // 节点流程任务组
    nodeTaskList: { type: Object, default: () => {} },
    // 画布高度
    height: { type: Number, default: 650 }
  },
  data () {
    return {
      modeler: null,
      zoom: 1,
      element: null,
      drawerVisible: false,
      currentTaskList: []
    }
  },
  watch: {
    xml: function (val) {
      if (val) {
        this.createDiagram()
      }
    },
    flowNodeList: function (val) {
      if (val) {
        this.createDiagram()
      }
    },
    nodeTaskList: function (val) {
      if (val) {
        this.createDiagram()
      }
    }
  },
  methods: {
    /**
     * 生成实例
     */
    initModeler () {
      this.modeler = new Modeler({
        container: this.$refs.canvas,
        additionalModules: [
          {
            translate: ['value', customTranslate],
            paletteProvider: ['value', ''], // 禁用/清空左侧工具栏
            labelEditingProvider: ['value', ''], // 禁用节点编辑
            contextPadProvider: ['value', ''], // 禁用图形菜单
            bendpoints: ['value', {}], // 禁用连线拖动
            // zoomScroll: ['value', ''], // 禁用滚动
            // moveCanvas: ['value', ''], // 禁用拖动整个流程图
            move: ['value', '']// 禁用单个图形拖动
          }
        ],
        moddleExtensions: {
          flowable: flowableModdle
        }
      })
    },
    /**
     * 创建流程图
     */
    async createDiagram () {
      if (!this.xml) {
        return
      }
      // 将字符串转换成图显示出来
      const data = this.xml.replace(/<!\[CDATA\[(.+?)]]>/g, function (match, str) {
        return str.replace(/</g, '&lt;')
      })
      await this.modeler.importXML(data)
      this.initSvg()
      this.fillColor()
      this.fitViewport()
    },

    /**
     * 给节点绑定事件 信息显示处理,
     */
    initEventBind () {
      const eventBus = this.modeler.get('eventBus')
      // 注册需要的监听事件
      const types = ['bpmn:UserTask', 'bpmn:StartEvent', 'bpmn:EndEvent']
      eventBus.on('element.hover', (eventObj) => {
        const element = eventObj ? eventObj.element : null
        if (types.includes(element.type)) {
          this.elementHover(element)
        }
      })
      eventBus.on('element.out', (eventObj) => {
        const element = eventObj ? eventObj.element : null
        if (types.includes(element.type)) {
          this.elementOut(element)
        }
      })
      eventBus.on('element.click', (eventObj) => {
        const element = eventObj ? eventObj.element : null
        if (element.type === 'bpmn:UserTask') {
          this.elementClick(element)
        }
      })
    },
    /**
     *  流程图的元素被 hover
     */
    elementHover (element) {
      this.element = element
      !this.elementOverlayIds && (this.elementOverlayIds = {})
      !this.overlays && (this.overlays = this.modeler.get('overlays'))

      let html = ''
      if (element.type === 'bpmn:StartEvent') {
        html = `<span>发起人：${this.instance.startUserName}</span>
                  <span>发起时间：${this.instance.startTime}</span>`
      }
      if (element.type === 'bpmn:UserTask') {
        const tasks = this.nodeTaskList[element.id]
        if (tasks) {
          const task = tasks[0]
          if (task) {
            html += `<span>执行人：${task.userName}</span>
                  <span>状态：${this.stateNameConvert(task.state)}</span>
                  <span>处理结果：${dictConvert('BpmTaskResult', task.result)}</span>
                  <span>结束时间：${task.endTime ? task.endTime : '' }</span>
                  <span>审批意见：${task.reason ? task.reason : ''}</span>`
          }
          if (tasks[1]) {
            html += `</br>
            <span>点击查看更多...</span>`
          }
        } else {
          return
        }
      }
      if (element.type === 'bpmn:EndEvent') {
        // 判断节点是否结束
        if (!this.instance.endTime) {
          return
        }
        html = `<span>结束时间：${ this.instance.endTime }</span>`
      }
      this.elementOverlayIds[element.id] = this.overlays.add(element, {
        position: { left: -50, bottom: 10 },
        html: `<div class="element-overlays">${html}</div>`
      })
    },
    /**
     * 流程图的元素被 out
     */
    elementOut (element) {
      this.overlays.remove({ element })
      this.elementOverlayIds[element.id] = null
    },
    /**
     * 点击事件
     */
    elementClick (element) {
      this.currentTaskList = this.nodeTaskList[element.id]
      if (this.currentTaskList) {
        this.drawerVisible = true
      }
    },
    /**
     * 处理SVG元素
     */
    initSvg () {
      // 添加完成箭头
      addArrow()
    },

    /**
     * 染色
     */
    fillColor () {
      const canvas = this.modeler.get('canvas')
      this.modeler._definitions.rootElements[0].flowElements.forEach(n => {
        const state = this.getNodeTaskState(n)
        if (['pass', 'skip'].includes(state)) {
          canvas.addMarker(n.id, 'highlight')
        } else if (state === 'running') {
          canvas.addMarker(n.id, 'highlight-todo')
        } else if (state === 'reject') {
          canvas.addMarker(n.id, 'highlight-reject')
        } else if (['back', 'cancel', 'retrieve'].includes(state)) {
          canvas.addMarker(n.id, 'highlight-cancel')
        } else {
          // 其他状态不进行处理
        }
      })
    },
    /**
     * 让图能自适应屏幕
     */
    fitViewport () {
      this.zoom = this.modeler.get('canvas').zoom('fit-viewport')
      const bbox = document.querySelector('.flow-containers .viewport').getBBox()
      const currentViewbox = this.modeler.get('canvas').viewbox()
      const elementMid = {
        x: bbox.x + bbox.width / 2 - 65,
        y: bbox.y + bbox.height / 2
      }
      this.modeler.get('canvas').viewbox({
        x: elementMid.x - currentViewbox.width / 2,
        y: elementMid.y - currentViewbox.height / 2,
        width: currentViewbox.width,
        height: currentViewbox.height
      })
      this.zoom = bbox.width / currentViewbox.width * 1.8
    },
    /**
     * 获取节点的任务状态
     */
    getNodeTaskState (node) {
      if (this.nodeTaskList) {
        const tasks = this.nodeTaskList[node.id]
        if (!tasks) {
          // 处理非用户任务节点
          return this.flowNodeList.find(m => m.activityId === node.id) ? 'pass' : ''
        }
        // 是否是多个
        if (tasks?.length > 1) {
          // 判断是否有执行中状态
          return tasks.find(t => t.state === 'running') ? 'running' : tasks[0].state
        } else {
          return tasks[0].state
        }
      }
    },
    /**
     * 任务状态翻译
     */
    stateNameConvert (state) {
      return dictConvert('BpmTaskState', state)
    },
    dictConvert (dictCode, code) {
      return dictConvert(dictCode, code)
    }
  },
  mounted () {
    this.initModeler()
    this.initEventBind()
  }
}
</script>

<style lang="less" scoped>
// Font class
@import "./icon/iconfont.css";
/*左边工具栏以及编辑节点的样式*/
@import "~bpmn-js/dist/assets/diagram-js.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";

@import "./css/ProcessViewerHighlight.less";
.view-mode {
  .ant-layout-header, .ant-layout-sider, /deep/.djs-palette, /deep/.bjs-powered-by {
    display: none;
  }
}
// 信息显示框
/deep/.element-overlays {
  box-sizing: border-box;
  padding: 8px;
  background: rgba(0, 0, 0, 0.6);
  border-radius: 4px;
  color: #fafafa;
  width: 230px;
}

html,body,#app{
  height:100%
}

.iconfont{
  margin-right: 5px;
}

.flow-containers {
  // background-color: #ffffff;
  width: 100%;
  height: 100%;
  .canvas {
    width: 100%;
    height: 100%;
  }
  .djs-palette{
    left: 0 !important;
    top: 0;
    border-top: none;
  }

  .djs-container svg {
    min-height: 650px;
  }
}

/deep/ .ant-layout-sider:hover{
  background: #0A97CE25 !important;
}

/deep/ .ant-layout-sider{
  z-index: 2;
}

/deep/ .djs-container {
  background: url() repeat !important;
}

</style>
